/*
 * Organizacion de computadoras (66.20) - TP1
 * 
 * TP1 1er Cuatrimestre 2013.
 * 
 * Integrantes:
 * 
 */
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include "Ordenamiento.h"
#include "ShellSort.h"
#include "BubbleSort.h"
#include "FileParser.h"

typedef void (*tComando) ();

/* help:
 *      Imprime al ayuda por pantalla.
 */
void help ();

/* version:
 *      Imprime el nombre de la aplicacion y la version por pantalla.
 */
void version();

/* getRutasArchivos:
 *      retorna un array de strings que representan las rutas de los archivos a 
 * procesar que se ingresan por parametro.
 * 
 * Nota: 
 *      se delega al cliente la responsabilidad de liberar la memoria que ocupa
 * la lista de palabras.
 */
char** getRutasArchivos (int cantidadArgumentos, char** argumentos, int indiceRutasArchivo);

/* imprimirPalabras:
 *      Imprime las palabras contenidas en el array de strings por salida
 * estandar.
 * 
 * Pre:
 *      cantidad debe ser un entero positivo.
 *      Si cantidad es cero, palabras debe ser NULL.  Caso contrario, palabras
 * debe contener la cantidad de strings especificada por cantidad.
 * 
 */
void imprimirPalabras (char** palabras, int cantidad);

//==============================================================================

int main (int argc, char **argv) {
    
    /* Se configura las opciones validas que acepta la aplicacion.
     * 
     */
    
    const char* const opcionesId = "hvsb" ;
    const struct option opciones[] =
    {
        { "help",       no_argument,  NULL,   'h'},
        { "version",    no_argument,  NULL,   'v'},
        { "sel",        no_argument,  NULL,   's'},
        { "bubble",     no_argument,  NULL,   'b'},
        { NULL,         no_argument,  NULL,   0  }
    };

    /* Se detectan los comandos/ordenamientos a ejecutar en funcion de los 
     * parametros de ejecucion.
     */
    tComando comando = NULL;
    tOrdenamiento ordenamiento = NULL;

    int opcionCursor = getopt_long (argc, argv, opcionesId, opciones, NULL);

    switch (opcionCursor){

        case 'h' : 
        case '?' : 
            comando = help;
            break;

        case 'v' : 
            comando = version;
            break;

        case 's' :
            ordenamiento = shellsort;
            break;

        case 'b' :
            ordenamiento = bubblesort;
            break;

        default :
            comando = help;
    }
    
    if (NULL != comando)
    {
        comando();
    }
    else if (NULL != ordenamiento)
    {
        int indice = optind;
        
        char** rutasArchivos = getRutasArchivos(argc, argv, indice);
        
        if (NULL != rutasArchivos)
        {
            int cursor = 0;
            while (NULL != rutasArchivos[cursor]){
                
                // Leo las palabras del archivo.
                tWords palabras = NULL;
                unsigned int cantidad = 0;
                fileParserFromPath(&palabras, &cantidad, rutasArchivos[cursor]);
                
                // Ordeno las palabras.
                ordenamiento (palabras, cantidad);
                
                // Imprimo las palabras por salida.
                imprimirPalabras (palabras, cantidad);
                
                // Libero la memoria correspondiente a las palabras.
                if (cantidad > 0)
                {
                    int i = 0;
                    for (i = 0; i < cantidad; i++)
                        free (palabras[i]);
                    free (palabras);
                }
                
                // Libero la memoria ocupada por la ruta del archivo.
                free (rutasArchivos[cursor]);
                
                cursor ++;
            }

            free (rutasArchivos);
        }
        else
        {
            char** palabras = NULL;
            int cantidad = 0;            
            fileParser (&palabras, &cantidad, stdin);
                
            // Ordeno las palabras.
            ordenamiento (palabras, cantidad);

            // Imprimo las palabras por salida.
            imprimirPalabras (palabras, cantidad);  
            
            if (cantidad > 0)
            {
                int i = 0;
                for (i = 0; i < cantidad; i++)
                    free (palabras[i]);
                free (palabras);
            }
        }
    }
    else
    {
        return (EXIT_FAILURE);
    }
    
    return (EXIT_SUCCESS);
} // main

//==============================================================================

void help (){

    printf("\ntp0 [OPTIONS][file...]");
    printf("\n");    
    printf("\n-h, --help      display this help and exit.");
    printf("\n-V, --version   display version information and exit.");
    printf("\n-m, --bubble    bubblesort algorithm.");
    printf("\n-s, --sel       shellsort algorithm.");    
    printf("\n");    

} // help

void version(){
    
    printf("\n\ntp0 version 0.2\n\n");

} // version

char** getRutasArchivos (int cantidadArgumentos, char** argumentos, int indiceRutasArchivo){
    
    char** archivos = NULL;
    
    if (indiceRutasArchivo < cantidadArgumentos){

        archivos = (char**) malloc ((cantidadArgumentos - indiceRutasArchivo + 1) * sizeof (char*));
        int i = 0, j = 0;
        
        for (i = indiceRutasArchivo, j = 0; i < cantidadArgumentos; i++, j++)
        {
            archivos[j] = (char*) malloc ( (strlen(argumentos[i]) + 1) * sizeof (char));
            strcpy (archivos[j], argumentos[i]);
        }
        
        archivos[j] = NULL;
    }
    
    return archivos;
}

void imprimirPalabras (char** palabras, int cantidad){

    if (NULL != palabras && cantidad > 0)
    {
        int i = 0;
        for (i = 0; i < cantidad; i++)
        {
            fprintf (stdout, "%s\n", palabras[i]);
        }
    }
}
